#include "uvlog.h"

namespace uv {
namespace log {

Log log;

void d(const char *tag, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    log.log(LOG_DEBUG, tag, fmt, args);
    va_end(args);
}

void i(const char *tag, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    log.log(LOG_INFO, tag, fmt, args);
    va_end(args);
}

void w(const char *tag, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    log.log(LOG_WARNING, tag, fmt, args);
    va_end(args);
}

void e(const char *tag, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    log.log(LOG_ERROR, tag, fmt, args);
    va_end(args);
}

bool openFile(const char *fname, const char *mode) {
    return log.openFile(fname, mode);
}

bool setFile(FILE *_fptr) {
    return log.setFile(_fptr);
}

Log::Log() : fptr(stdout), is_file_owned(false) {
    pthread_mutex_init(&fptr_mutex, NULL);
}

Log::~Log() {
    pthread_mutex_lock(&fptr_mutex);
    closeOwnedFile();
    pthread_mutex_unlock(&fptr_mutex);

    pthread_mutex_destroy(&fptr_mutex);
}

bool Log::openFile(const char *fname, const char *mode) {
    FILE *tmp = fopen(fname, mode);
    if (tmp != NULL) {
        pthread_mutex_lock(&fptr_mutex);
        closeOwnedFile();
        fptr = tmp;
        is_file_owned = true;
        pthread_mutex_unlock(&fptr_mutex);
        return true;
    }
    return false;
}

bool Log::setFile(FILE *_fptr) {
    if (_fptr && (_fptr == stdout || _fptr == stdin || ftell(_fptr) >= 0)) {
        pthread_mutex_lock(&fptr_mutex);
        closeOwnedFile();
        fptr = _fptr;
        is_file_owned = false;
        pthread_mutex_unlock(&fptr_mutex);
        return true;
    }
    return false;
}

void Log::log(Type_t type, const char *tag, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    log(type, tag, fmt, args);
    va_end(args);
}

void Log::log(Type_t type, const char *tag, const char *fmt, va_list args) {
    pthread_mutex_lock(&fptr_mutex);
    printType(type);
    fprintf(fptr, "%s ", tag);
    vfprintf(fptr, fmt, args);
    fprintf(fptr, "\n");
    pthread_mutex_unlock(&fptr_mutex);
}

void Log::printType(Type_t type) {
    switch (type) {
        case LOG_DEBUG:
            fprintf(fptr, "DEBUG ");
            break;
        case LOG_INFO:
            fprintf(fptr, "INFO ");
            break;
        case LOG_WARNING:
            fprintf(fptr, "WARNING ");
            break;
        case LOG_ERROR:
            fprintf(fptr, "ERROR ");
            break;
    }
}

void Log::closeOwnedFile() {
    if (is_file_owned) {
        fclose(fptr);
        fptr = stdout;
        is_file_owned = false;
    }
}

} // namespace log
} // namespace uv
